<template>
	<el-dialog title="导入分组" :visible.sync="showDialog" :before-close="cancel" width="10rem">
		<div>
			<span>填写模版文件，表头不可修改及删除</span>
			<el-link type="primary" class="detail-link" style="padding: 0 5px" :underline="false" :href="excelUrl" target="_blank">
				<el-button class="download-button"><i class="el-icon-download el-icon--left"></i>下载模版</el-button>
			</el-link>
		</div>
		<br />
		<el-upload
			v-loading="loading"
			element-loading-text="数据校验中..."
			element-loading-spinner="el-icon-loading"
			element-loading-background="rgba(255, 255, 255, 0.8)"
			accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
			ref="uploadRef"
			:on-remove="onRemoveUploadFile"
			:on-change="onFileChange"
			@drop.native="e => dragToUpload(e)"
			:file-list="uploadFileList"
			class="upload-demo"
			style="align-items: center; display: flex; flex-direction: column;"
			drag
			:auto-upload="false"
			:on-progress="onUploadProgress"
			:action="uploadFileConfig.url"
			:headers="uploadFileConfig.headers"
			:show-file-list="false"
			:on-exceed="onAnotherFile"
			limit="1">
			<div class="el-upload__tip" slot="tip"></div>
			<div class="el-upload__trigger before-upload-box" v-if="uploadFileList.length === 0" slot="trigger">
				<i class="el-icon-excel"></i>
				<div class="el-upload__text upload-text">点击或拖拽上传，仅支持Excel文件，不超过1000行</div>
			</div>
			<div class="upload-box" v-else slot="trigger">
				<div class="upload-items">
					<i class="el-icon-excel excel-img"></i>
					<div class="el-upload__text excel-name">{{ this.uploadFileList[0].name }}</div>
					<i class="el-icon-close excel-remove" @click="removeUploadFile($event)"></i>
				</div>
			</div>
		</el-upload>
		<span v-show="uploadFileList.length === 0 && clickedSubmit" style="color: red;">请选择导入文件</span>
		<br />
		<div class="btns">
			<el-button @click="cancel">取消</el-button>
			<el-button type="primary" @click="submit($event)">确认</el-button>
		</div>
	</el-dialog>
</template>

<script>
import { mapState } from 'vuex';
import { fixUploadPercent, forceBtnBlur } from '@/utils/MyVueUtil';
import XLSX from 'xlsx';
import { getUploadFileConfig, getAllGroups, importGroups } from '@/api/api';
import { getValidateGroupNameResult } from '@/utils/formValidate';

export default {
	components: {},
	props: {
		show: {
			type: Boolean,
			default: false
		}
	},
	data() {
		return {
			clickedSubmit: false,
			excelUrl: importGroupTemplateUrl,
			uploadFileList: [],
			uploadFileConfig: getUploadFileConfig(),
			loading: false,
			showDialog: false,
			existedGroups: [],
			groups: []
		};
	},
	watch: {
		show(newVal) {
			this.showDialog = newVal;
			if (newVal) {
				this.refreshAllGroups();
			}
		}
	},
	computed: {
		...mapState({
			loginInfo: state => state.login.userInfo
		})
	},
	mounted() {
	},
	methods: {
		reset() {
			this.uploadFileList = [];
			this.$refs.uploadRef.clearFiles();
			this.clickedSubmit = false;
			this.loading = false;
		},
		removeUploadFile(e) {
			this.reset();
			e.stopPropagation();
		},
		refreshAllGroups() {
			getAllGroups().then(res => {
				this.existedGroups = res.data;
			}).catch (error => {
				this.$message.error('获取分组列表失败！');
				console.log('getAllGroups failed:', error);
			});
		},
		submit(event) {
			forceBtnBlur(event);
			if (this.uploadFileList.length > 0) {
				this.reset();
				importGroups(this.groups).then(res => {
					this.$emit('importGroupsSuccess');
					this.$message.success('导入成功！');
				}).catch (error => {
					this.refreshAllGroups();
					switch(error.code + '') {
						case '1002':
							this.$message.error(`分组 <${error.data.groupName}> 已存在！`);
							break;
						default:
							this.$message.error(error);
							break;
					}
				});
			} else {
				this.clickedSubmit = true;
				// this.$message.warning('请先上传 Excel 文件！')
			}
		},
		cancel() {
			this.reset();
			this.$emit('close');
		},
		onRemoveUploadFile(file, fileList) {
			this.uploadFileList = [];
		},
		dragToUpload(e){
			console.log('drop event:', e.dataTransfer.files[0]);
			this.onFileChange({'raw': e.dataTransfer.files[0], 'size': e.dataTransfer.files[0].size, 'name': e.dataTransfer.files[0].name}, null);
		},
		onAnotherFile(files, fileList) {
			console.log('1:', this.uploadFileList);
			console.log('2:', files);
			if (this.uploadFileList[0].name === files[0].name && 
				this.uploadFileList[0].lastModified === files[0].lastModified &&
				this.uploadFileList[0].size === files[0].size) {
				// 是之前上传的文件再做处理
			} else {
				this.onFileChange({'raw': files[0], 'size': files[0].size, 'name': files[0].name}, null);
			}
		},
		onFileChange(file, fileList) {
			this.loading = true;
			const isExcel = file.raw.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
			if (!isExcel) {
				this.$message.error('格式不正确，仅支持Excel文件导入');
				this.reset();
				return;
			}
			if (file.size > excelMaxSize) {
				const size = Math.floor(excelMaxSize / 1024 / 1024);
				this.$message.error(`请上传小于${size}M的文件！`);
				this.reset();
				return;
			}
			// 这里稍微延迟一下，让 loading 效果展示出来，否则在读取比较耗时的 Excel 文件时，loading 效果有几率展示不出来
			setTimeout(() => {
				let fileReader = new FileReader();
				fileReader.readAsBinaryString(file.raw);
				fileReader.onload = (e) => {
					this.groups = [];// 用于存放提交的组名
					// 读取 excel 表格中数据
					let data = e.target.result;
					let workbook = XLSX.read(data, {type: 'binary'});
					let sheetNames = workbook.SheetNames;
					if (sheetNames.length !== 1) {
						this.$message.error('Excel 中只允许有一张表');
						this.reset();
						return;
					}
					// excel 中表格表头，必须按照 excel 表头顺序存放
					let tempKeys = [ '分组名'];
					let firstSheet = XLSX.utils.sheet_to_json(workbook.Sheets[sheetNames[0]], { blankrows: true, defval: ''});
					if (firstSheet.length === 0) {
						this.$message.error('Excel 中没有数据！');
						this.reset();
						return;
					}
					if (firstSheet.length > 1000) {
						console.log('excel groups sheet:', firstSheet);
						this.$message.error('Excel 中最大只允许 1000 条数据！');
						this.reset();
						return;
					}
					// 校验表格中各个字段后的结果汇总
					let checkResult = '';
					// 校验 excel 表格的表头是否符合格式
					let keys = Object.keys(firstSheet[0]);
					for (let i = 0; i < tempKeys.length; i++){
						if (tempKeys[i] !== keys[i]) {
							checkResult +=  `表单第 ${ String.fromCharCode( i + 65) } 列，应该为 ${ tempKeys[i] }<br />`;
						}
					}
					// excel 符合规范后再校验每列
					if (checkResult.length === 0) {
						// 用于统一处理校验结果，result 校验失败原因
						let handleCheckResult = (row, col, result) => {
							if (result.length !== 0) {
								checkResult +=  `表单第 ${ row + 2 } 行, 第 ${ String.fromCharCode( col + 65) } 列，${ result }<br />`;
							}
						}
						// 表格中重复的组名
						let dulplicatedGroupNames = {};
						// 和系统中已存在的组名重复的组名
						let dulplicatedGroupNameToExist = {};
						this.existedGroups.forEach(value => {
							dulplicatedGroupNameToExist[value.name] = [];
						});
						// 遍历表格每行的数据
						firstSheet.forEach((value, index) => {
							for (let i = 0; i < tempKeys.length; i++) {
								switch(tempKeys[i]) {
									case '分组名':
										let groupName = value[tempKeys[i]] + '';
										if (groupName.length > 0) {
											dulplicatedGroupNames[groupName] = dulplicatedGroupNames[groupName] || [];
											dulplicatedGroupNames[groupName].push(index + 2);
											dulplicatedGroupNameToExist[groupName]?.push(index + 2);
											// 由于分组表中只有一个字段，当有空值时可以跳过
											this.groups.push(groupName);
											handleCheckResult(index, i, getValidateGroupNameResult(groupName));
										}
										break;
								}
							}
						});
						// 检测重复组名的结果
						let dulplicatedGroupNamesResult = '';
						// excel 中重复的组名
						Object.keys(dulplicatedGroupNames).forEach((value, index) => {
							let rows = dulplicatedGroupNames[value];
							if (rows?.length > 1) {
								dulplicatedGroupNamesResult += `表单中第 ${rows} 行，&lt;${value}&gt;分组重复！<br />`
							}
						});
						// excel 中与系统中已有的组名重复的
						Object.keys(dulplicatedGroupNameToExist).forEach((value, index) => {
							let rows = dulplicatedGroupNameToExist[value];
							if (rows?.length > 0) {
								dulplicatedGroupNamesResult += `表单中第 ${rows} 行，与系统中的&lt;${value}&gt;分组重复！<br />`
							}
						});
						// 添加重复组名信息
						if (dulplicatedGroupNamesResult.length > 0){
							checkResult = `<b>分组重复：</b><br />${dulplicatedGroupNamesResult}<br />${checkResult.length > 0 ? `<b>格式错误：</b><br />` :  ''}${checkResult}`;
						}
					} else {
						checkResult = '<b>请勿修改表格表头信息，请重新编辑后再导入！</b><br />' + checkResult;
					}
					// 关闭校验动画
					this.loading = false;
					// 处理校验结果
					if (checkResult.length === 0) {
						if (this.groups.length === 0) {
							this.reset();
							this.$message.warning('Excel 中没有数据！')
						} else {
							this.uploadFileList = [{name: file.name, lastModified: file.raw.lastModified, size: file.size}];
							this.$message.success('数据校验成功！');
						}
					} else {
						this.reset();
						this.$alert(`<div style="overflow-y:scroll;overflow-x:hidden;max-height:300px">${checkResult}</div>`, 'Excel 检测失败', {
							confirmButtonText: '确定',
							dangerouslyUseHTMLString: true
						});
					}
				};
			}, 100);
		},
		onUploadProgress(event, file, fileList) {
			fixUploadPercent(this, event, file, fileList);
		},
	}
};
</script>

<style lang="less">
.btns {
	text-align: right;
}
.account {
	.head {
		padding: 20px 0;
	}
	.el-avatar > img {
		width: 100%;
	}
}
</style>

<style lang="less" scoped>
.detail-link {
	/deep/ .el-button {
		width: 80px;
		height: 28px;
		background: #ffffff;
		border: 1px solid #6384fe;
		border-radius: 4px;
		font-size: 12px;
		font-weight: 400;
		color: #6384fe;
		line-height: 28px;
		padding: 0;
	}
}
.download-button {
	
}
.before-upload-box {
	display: flex;
	flex-direction: column;
	height: 100%;
	justify-content: center;
	.upload-text {
		margin-top: 10px;
	}
}
.upload-box {
	display: flex;
	align-items: center; 
	height: 100%;
	.upload-items {
		display: flex;
		flex-direction: row;
		align-items: center;
		padding-left: 10px;
		padding-right: 10px;
		justify-content: center;
		width: 100%;
		.excel-img {
			margin: 0 0 0 0;
		}
		.excel-name {
			margin: 0 0 0 10px;
		}
		.excel-remove {
			padding: 10px 10px 10px 10px;
		}
	}
}
</style>

